#include <iostream>
#include <poll.h>
#include "Sock.hpp"

#define NUM 1024
struct pollfd fdsArray[NUM] = {0};// 保存历史上所有的合法fd

#define DFL -1

using namespace std;

static void showArray(struct pollfd arr[], int num)
{
    cout << "当前合法sock list# ";
    for (int i = 0; i < num; i++)
    {
        if (arr[i].fd == DFL)
            continue;
        else
            cout << arr[i].fd << " ";
    }
    cout << endl;
}

static void usage(std::string process)
{
    cerr << "\nUsage: " << process << " port\n"
         << endl;
}
// readfds: 现在包含就是已经就绪的sock
static void HandlerEvent(int listensock)
{
    for (int i = 0; i < NUM; i++)
    {
        if (fdsArray[i].fd == DFL)
            continue;
        if (i == 0 && fdsArray[i].fd == listensock)
        {
            // 我们是如何得知哪些fd，上面的事件就绪呢？
            if (fdsArray[i].revents & POLLIN)
            {
                // 具有了一个新链接
                cout << "已经有一个新链接到来了，需要进行获取(读取/拷贝)了" << endl;
                string clientip;
                uint16_t clientport = 0;
                int sock = Sock::Accept(listensock, &clientip, &clientport); // 不会阻塞
                if (sock < 0)
                    return;
                cout << "获取新连接成功: " << clientip << ":" << clientport << " | sock: " << sock << endl;

                // read/write -- 不能，因为你read不知道底层数据是否就绪！！select知道！
                // 想办法把新的fd托管给select？如何托管？？
                int i = 0;
                for (; i < NUM; i++)
                {
                    if (fdsArray[i].fd == DFL)
                        break;
                }
                if (i == NUM)
                {
                    cerr << "我的服务器已经到了最大的上限了，无法在承载更多同时保持的连接了" << endl;
                    close(sock);
                }
                else
                {
                    fdsArray[i].fd = sock; // 将sock添加到select中，进行进一步的监听就绪事件了！
                    fdsArray[i].events = POLLIN;
                    fdsArray[i].revents = 0;
                    showArray(fdsArray, NUM);
                }
            }
        } // end if (i == 0 && fdsArray[i] == listensock)
        else
        {
            // 处理普通sock的IO事件！
            if(fdsArray[i].revents & POLLIN)
            {
                // 一定是一个合法的普通的IO类sock就绪了
                // read/recv读取即可
                // TODO bug
                char buffer[1024];
                ssize_t s = recv(fdsArray[i].fd, buffer, sizeof(buffer), 0); // 不会阻塞
                if(s > 0)
                {
                    buffer[s] = 0;
                    cout << "client[" << fdsArray[i].fd << "]# " << buffer << endl; 
                }
                else if(s == 0)
                {
                    cout << "client[" << fdsArray[i].fd << "] quit, server close " << fdsArray[i].fd << endl;
                    close(fdsArray[i].fd);
                    fdsArray[i].fd = DFL; // 去除对该文件描述符的select事件监听
                    fdsArray[i].events = 0;
                    fdsArray[i].revents = 0;
                    showArray(fdsArray, NUM);
                }
                else
                {
                    cout << "client[" << fdsArray[i].fd << "] error, server close " << fdsArray[i].fd << endl;
                    close(fdsArray[i].fd);
                    fdsArray[i].fd = DFL; // 去除对该文件描述符的select事件监听
                    fdsArray[i].events = 0;
                    fdsArray[i].revents = 0;
                    showArray(fdsArray, NUM);
                }
            }
        }
    }
}

// ./SelectServer 8080
// 只关心读事件
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }
    // 是一种类型，位图类型，能定义变量，那么就一定有大小，就一定有上限
    // fd_set fds; // fd_set是用位图表示多个fd的
    // cout << sizeof(fds) * 8 << endl;
    int listensock = Sock::Socket();
    Sock::Bind(listensock, atoi(argv[1]));
    Sock::Listen(listensock);

    for (int i = 0; i < NUM; i++)
    {
        fdsArray[i].fd = DFL;
        fdsArray[i].events = 0;
        fdsArray[i].revents = 0;
    }
    fdsArray[0].fd = listensock;
    fdsArray[0].events = POLLIN;
    int timeout = -1;
    while (true)
    {
        int n = poll(fdsArray, NUM, timeout);
        switch (n)
        {
        case 0:
            cout << "time out ... : " << (unsigned long)time(nullptr) << endl;
            break;
        case -1:
            cerr << errno << " : " << strerror(errno) << endl;
            break;
        default:
            HandlerEvent(listensock);
            // 等待成功
            // 1. 刚启动的时候，只有一个fd，listensock
            // 2. server 运行的时候，sock才会慢慢变多
            // 3. select 使用位图，采用输出输出型参数的方式，来进行 内核<->用户 信息的传递， 每一次调用select，都需要对历史数据和sock进行重新设置！！！
            // 4. listensock，永远都要被设置进readfds中！
            // 5. select 就绪的时候，可能是listen 就绪，也可能是普通的IO sock就绪啦！！
            break;
        }
    }

    return 0;
}